﻿using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Serilog;
using Serilog.Configuration;
using Serilog.Context;
using Serilog.Events;
using Serilog.Formatting.Compact;
using Serilog.Formatting.Display;
using Serilog.Formatting.Json;
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Text;
using NetMicro.Trace.Const;
using NetMicro.Trace.Email;
using NetMicro.Trace.Enricher;
using NetMicro.Trace.Enums;
using NetMicro.Trace.Options;

namespace NetMicro.Trace.Extensions
{
    /// <summary>
    /// 日志扩展
    /// </summary>
    public static class LoggingExtensions
    {
        /// <summary>
        /// 日志等级转换
        /// </summary>
        /// <param name="logLevel"></param>
        /// <returns></returns>
        public static LogEventLevel TryLogLevelSerilogConvert(this LogLevel logLevel)
        {
            switch (logLevel)
            {
                case LogLevel.Trace:
                    return LogEventLevel.Verbose;
                case LogLevel.Debug:
                    return LogEventLevel.Debug;
                case LogLevel.Information:
                    return LogEventLevel.Information;
                case LogLevel.Warning:
                    return LogEventLevel.Warning;
                case LogLevel.Error:
                    return LogEventLevel.Error;
                case LogLevel.Critical:
                    return LogEventLevel.Fatal;
                case LogLevel.None:
                    return LogEventLevel.Fatal;
                default:
                    return LogEventLevel.Fatal;
            }
        }

        /// <summary>
        /// 添加serilog日志等级转换
        /// </summary>
        /// <param name="build"></param>
        /// <param name="loggingOption"></param>
        /// <returns></returns>
        public static LoggerConfiguration AddSerilogFilter(this LoggerConfiguration build, LoggingOption loggingOption)
        {
            foreach (var item in loggingOption.LogLelvel)
            {
                if (item.Key == Penetrates.Default)
                    continue;
                build.MinimumLevel.Override(item.Key, item.Value.TryLogLevelSerilogConvert());
            }
            return build;
        }

        /// <summary>
        /// 添加serilog日志等级转换
        /// </summary>
        /// <param name="build"></param>
        /// <param name="loggingOption"></param>
        /// <returns></returns>
        public static LoggerConfiguration AddSerilogLelvel(this LoggerConfiguration build, LoggingOption loggingOption)
        {
            loggingOption.LogLelvel.TryGetValue(Penetrates.Default, out LogLevel logLevel);
            switch (logLevel)
            {
                case LogLevel.Trace:
                    return build.MinimumLevel.Verbose();
                case LogLevel.Debug:
                    return build.MinimumLevel.Debug();
                case LogLevel.Information:
                    return build.MinimumLevel.Information();
                case LogLevel.Warning:
                    return build.MinimumLevel.Warning();
                case LogLevel.Error:
                    return build.MinimumLevel.Error();
                case LogLevel.Critical:
                    return build.MinimumLevel.Fatal();
                case LogLevel.None:
                    return build.MinimumLevel.Fatal();
                default:
                    return build.MinimumLevel.Fatal();
            }
        }

        /// <summary>
        /// 写入Text结构化日志
        /// </summary>
        /// <param name="build"></param>
        /// <param name="loggingOption"></param>
        /// <returns></returns>
        public static LoggerConfiguration WriteSerilogText(this LoggerConfiguration build, LoggingOption loggingOption)
        {

            if (loggingOption.WriteToType == WriteToType.Text || loggingOption.WriteToType == WriteToType.TextWithConsole)
            {
                build.WriteTo.Map(le => new Tuple<DateTime, LogEventLevel>(new DateTime(le.Timestamp.Year, le.Timestamp.Month, le.Timestamp.Day), le.Level),
                          (key, log) => log.Async(a => a.File(Path.Combine(loggingOption.LogPath, $"{key.Item1:yyyy-MM}/{key.Item2.ToString().ToLower()}-.log"), rollingInterval: RollingInterval.Day, retainedFileCountLimit: null, outputTemplate: Penetrates.DefaultOutputTemplate)),
                      sinkMapCountLimit: 1);

            }
            return build;

        }

        /// <summary>
        /// 写入Json结构化日志
        /// </summary>
        /// <param name="build"></param>
        /// <param name="loggingOption"></param>
        /// <returns></returns>
        public static LoggerConfiguration WriteSerilogJson(this LoggerConfiguration build, LoggingOption loggingOption)
        {
            if (loggingOption.WriteToType == WriteToType.Json || loggingOption.WriteToType == WriteToType.JsonWithConsole)
            {
                build.WriteTo.Map(le => new Tuple<DateTime, LogEventLevel>(new DateTime(le.Timestamp.Year, le.Timestamp.Month, le.Timestamp.Day), le.Level),
                          (key, log) => log.Async(a => a.File(formatter: new CompactJsonFormatter(), Path.Combine(loggingOption.LogPath, $"{key.Item1:yyyy-MM}/{key.Item2.ToString().ToLower()}-.log"), rollingInterval: RollingInterval.Day, retainedFileCountLimit: null)),
                      sinkMapCountLimit: 1);

            }
            return build;

        }

        /// <summary>
        /// 写入控制台日志
        /// </summary>
        /// <param name="build"></param>
        /// <param name="loggingOption"></param>
        /// <returns></returns>
        public static LoggerConfiguration WriteSerilogConsole(this LoggerConfiguration build, LoggingOption loggingOption)
        {
            if (loggingOption.WriteToType == WriteToType.Console || loggingOption.WriteToType == WriteToType.TextWithConsole)
            {
                build.WriteTo.Console(outputTemplate: Penetrates.DefaultOutputTemplate);
            }
            return build;
        }

        /// <summary>
        /// 发送邮件
        /// </summary>
        /// <param name="build"></param>
        /// <param name="loggingOption"></param>
        /// <returns></returns>
        public static LoggerConfiguration SendEmail(this LoggerConfiguration build, LoggingOption loggingOption)
        {
            if (loggingOption.ErrorEmail.IsWriteToEmail)
            {
                build.WriteTo.Email(new EmailConnectionInfo()
                {
                    EmailSubject = loggingOption.ErrorEmail.Subject,//邮件标题
                    FromEmail = loggingOption.ErrorEmail.FromEmail,//发件人邮箱
                    MailServer = loggingOption.ErrorEmail.MailServer,//smtp服务器地址
                    NetworkCredentials = new NetworkCredential(loggingOption.ErrorEmail.FromEmail, loggingOption.ErrorEmail.AuthCode),//两个参数分别是发件人邮箱与客户端授权码
                    Port = loggingOption.ErrorEmail.Port,//端口号
                    ToEmail = loggingOption.ErrorEmail.ToEmail//收件人
                }, restrictedToMinimumLevel: LogEventLevel.Error);
            }
            return build;
        }



        /// <summary>
        /// 添加Activity
        /// </summary>
        /// <param name="loggerEnrichmentConfiguration"></param>
        /// <returns></returns>
        public static LoggerConfiguration AddActivity(this LoggerEnrichmentConfiguration loggerEnrichmentConfiguration)
        {
            ArgumentNullException.ThrowIfNull(loggerEnrichmentConfiguration);
            loggerEnrichmentConfiguration.With<ActivityTagEnricher>();
            return loggerEnrichmentConfiguration.With<ActivityEnricher>();
        }


    }
}
